import { MoveToModeCommand } from "../command/mode.command"
import transitionEnum from "../enum/transition.enum"
import viewmodeEventEnum from "../enum/viewmodeEvent.enum"
import Logger from "../utils/Logger"
import { BaseException, isPositon } from "./utils"
import * as THREE from "three"
class ViewmodeActiveTransitionError extends BaseException {
  constructor(e = "Tried to start view-mode transition while another transition was active") {
    super(e), (this.name = "ViewmodeActiveTransition")
  }
}
const allowNum = function (num, min, max) {
  return Math.max(min, Math.min(num, max))
}

class MoveToModeValidation {
  THREE: typeof THREE
  CmdViewmode: any
  conversion: any
  transitionTypeConverter: any
  constructor(e: typeof THREE, t, o, n) {
    this.THREE = e
    this.CmdViewmode = t
    this.conversion = o
    this.transitionTypeConverter = n
  }
  validateMoveToModeInput(e) {
    if (!e) throw Error("Mode.moveTo -> no arguments provided -- mode is required")
    e.options || (e.options = {})
    const t = e.mode
    if (!Object.values(this.CmdViewmode).includes(e.mode)) throw Error(`Mode.moveTo -> ${e.mode} is not a valid viewmode`)
    if (t === this.CmdViewmode.TRANSITIONING) throw Error(`Mode.moveTo -> ${e.mode} is not a valid viewmode`)
    e.options && e.options.rotation && (e.options.rotation.z = e.options.rotation.z || 0)
    const o = this.validatePayloadOptions(e.options)
    return {
      mode: t,
      options: { transition: o.transition, position: o.position, rotation: o.rotation, zoom: o.zoom }
    }
  }
  validatePayloadOptions(e) {
    let t, o, n, r
    if (e) {
      if (e.transition && ((n = this.transitionTypeConverter.fromSdkTransition(e.transition)), void 0 === n))
        throw Error(`Mode.moveTo -> ${e.transition} is not a valid transition style`)
      e.rotation && isPositon(e.rotation) && (t = this.conversion.rotationToQuaternion(e.rotation)),
        e.position && isPositon(e.position) && (o = new this.THREE.Vector3(parseFloat(e.position.x), parseFloat(e.position.y), parseFloat(e.position.z))),
        e.zoom && !isNaN(e.zoom) && (r = allowNum(e.zoom, 1, 30))
    }
    return { rotation: t, position: o, transition: n, zoom: r }
  }
}
export default {
  Event: sdk => {
    sdk.addEnumToInterface({ namespace: "Mode", name: "Event", values: viewmodeEventEnum })
  },
  moveTo: (sdk, THREE, exportOBJ, allFunc) => {
    const log = new Logger("move-to-mode-command")
    allFunc.addBinding(MoveToModeCommand, async e => {
      try {
        e.options = e.options || {}
        let t = exportOBJ.CameraTransitionType.Interpolate
        const r = e.options.transition
        return (
          void 0 === r || isNaN(r) || (t = r),
          await allFunc.issueCommand(
            new exportOBJ.ChangeViewmodeCommand(e.mode, t, {
              position: e.options.position,
              rotation: e.options.rotation,
              zoom: e.options.zoom
            })
          ),
          e.mode
        )
      } catch (t) {
        const o =
          t instanceof ViewmodeActiveTransitionError
            ? `Mode.moveTo -> Cannot move to ${e.mode} during a mode transition`
            : `Mode.moveTo -> Could not move to ${e.mode}`
        if ((log.error(t, o), t instanceof ViewmodeActiveTransitionError)) throw Error(`Mode.moveTo -> Cannot move to ${e.mode} during a mode transition`)
        throw Error(`Mode.moveTo -> Could not move to ${e.mode}`)
      }
    })
    const s = new MoveToModeValidation(THREE, exportOBJ.CommandViewmode, allFunc.conversionUtils, allFunc.cameraTransitionConverter)
    sdk.addCommandCreator({ namespace: "Mode", name: "moveTo", args: ["mode", "options"] }, e => new MoveToModeCommand(s.validateMoveToModeInput(e)))
  },
  Mode: (sdk, exportOBJ) => {
    sdk.addEnumToInterface({ namespace: "Mode", name: "Mode", values: exportOBJ.CommandViewmode })
  },
  TransitionType: sdk => {
    sdk.addEnumToInterface({ namespace: "Mode", name: "TransitionType", values: transitionEnum })
  },
  onEvent: (sdk, exportOBJ, allFunc, dependencies) => {
    dependencies.getDependencies().then(([n, r]) => {
      const a = [void 0, void 0],
        s = e => {
          const t = r.currentMode,
            s = allFunc.sweepUtils.isSweepAligned(n, e.fromSweep),
            i = allFunc.sweepUtils.isSweepAligned(n, e.toSweep)
          return (a[0] = allFunc.viewmodeConverter.toSdk(t, s)), (a[1] = allFunc.viewmodeConverter.toSdk(t, i)), a
        }
      allFunc.subscribe(exportOBJ.EndMoveToSweepMessage, t => {
        allFunc.sweepUtils.isSweepAligned(n, t.fromSweep) !== allFunc.sweepUtils.isSweepAligned(n, t.toSweep) &&
          sdk.broadcast(viewmodeEventEnum.CHANGE_END, s, t)
      })
      const i = [void 0, void 0],
        d = e => {
          const t = allFunc.sweepUtils.isCurrentSweepAligned(n)
          return (i[0] = allFunc.viewmodeConverter.toSdk(e.fromMode, t)), (i[1] = allFunc.viewmodeConverter.toSdk(e.toMode, t)), i
        }
      allFunc.subscribe(exportOBJ.BeginSwitchViewmodeMessage, t => sdk.broadcast(viewmodeEventEnum.CHANGE_START, d, t))
      const c = [void 0, void 0],
        u = e => {
          const t = allFunc.sweepUtils.isCurrentSweepAligned(n)
          return (c[0] = allFunc.viewmodeConverter.toSdk(e.fromMode, t)), (c[1] = allFunc.viewmodeConverter.toSdk(e.toMode, t)), c
        }
      allFunc.subscribe(exportOBJ.EndSwitchViewmodeMessage, t => sdk.broadcast(viewmodeEventEnum.CHANGE_END, u, t))
    })
  }
}
